/**
 * 
 */
package com.dx.pf.commons.number;

import java.math.BigDecimal;

/**
 * @ClassName: DoubleUtil
 * @Description: 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精确的浮点数运算，包括加减乘除和四舍五入。
 * @author wuzhenfang(wuzhenfang@daojia.com)
 * @date 2016年6月28日 上午10:11:46
 * @version V1.0
 */
public class DoubleUtil {

	// 默认除法运算精度，有效数字
	private static final int DEF_DIV_SCALE = 15;// 使用double默认精度
	// 这个类不能实例化

	private DoubleUtil() {
	}

	/**
	 * 功能说明：提供精确的加法运算。
	 * @param v1 被加数
	 * @param v2 加数
	 * @return 两个参数的和
	 */
	public static final double add(double v1, double v2) {
		return add(v1, v2, 0, 0, 0, 0, 2);
	}

	/**
	 * 功能说明：提供精确的加法运算。
	 * @param v1 被加数
	 * @param v2 加数
	 * @param v3 加数
	 * @return 三个参数的和
	 */
	public static final double add(double v1, double v2, double v3) {
		return add(v1, v2, v3, 0, 0, 0, 3);
	}

	/**
	 * 功能说明：提供精确的加法运算。
	 * @param v1 被加数
	 * @param v2 加数
	 * @param v3 加数
	 * @param v4 加数
	 * @return 四个参数的和
	 */
	public static final double add(double v1, double v2, double v3, double v4) {
		return add(v1, v2, v3, v4, 0, 0, 4);
	}

	/**
	 * 功能说明：提供精确的加法运算。
	 * @param v1 被加数
	 * @param v2 加数
	 * @param v3 加数
	 * @param v4 加数
	 * @param v5 加数
	 * @return 五个参数的和
	 */
	public static final double add(double v1, double v2, double v3, double v4, double v5) {
		return add(v1, v2, v3, v4, v5, 0, 5);
	}

	/**
	 * 功能说明：提供精确的加法运算。
	 * @param v1 被加数
	 * @param v2 加数
	 * @param v3 加数
	 * @param v4 加数
	 * @param v5 加数
	 * @param v6 加数
	 * @return 六个参数的和
	 */
	public static final double add(double v1, double v2, double v3, double v4, double v5, double v6) {
		return add(v1, v2, v3, v4, v5, v6, 6);
	}

	/**
	 * 功能说明：提供精确的加法运算(此方法为底层调用方法)。
	 * @param v1 被加数
	 * @param v2 加数
	 * @param v3 加数
	 * @param v4 加数
	 * @param v5 加数
	 * @param v6 加数
	 * @param n 参数个数
	 * @return n个参数的和
	 */
	private static final double add(double v1, double v2, double v3, double v4, double v5, double v6, int n) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		BigDecimal b3 = null;
		BigDecimal b4 = null;
		BigDecimal b5 = null;
		BigDecimal b6 = null;

		switch (n) {
		case 2:
			return b1.add(b2).doubleValue();
		case 6:
			b6 = new BigDecimal(Double.toString(v6));
		case 5:
			b5 = new BigDecimal(Double.toString(v5));
		case 4:
			b4 = new BigDecimal(Double.toString(v4));
		case 3:
			b3 = new BigDecimal(Double.toString(v3));
		}

		switch (n) {
		case 3:
			return b1.add(b2).add(b3).doubleValue();
		case 4:
			return b1.add(b2).add(b3).add(b4).doubleValue();
		case 5:
			return b1.add(b2).add(b3).add(b4).add(b5).doubleValue();
		case 6:
			return b1.add(b2).add(b3).add(b4).add(b5).add(b6).doubleValue();
		default:
			return 0;
		}
	}

	/**
	 * 功能说明：提供精确的减法运算。
	 * @param v1 被减数
	 * @param v2 减数
	 * @return 两个参数的差
	 */
	public static final double sub(double v1, double v2) {
		return add(v1, -v2, 0, 0, 0, 0, 2);
	}

	/**
	 * 功能说明：提供精确的减法运算。
	 * @param v1 被减数
	 * @param v2 减数
	 * @param v3 减数
	 * @return 三个参数的差
	 */
	public static final double sub(double v1, double v2, double v3) {
		return add(v1, -v2, -v3, 0, 0, 0, 3);
	}

	/**
	 * 功能说明：提供精确的减法运算。
	 * @param v1 被减数
	 * @param v2 减数
	 * @param v3 减数
	 * @param v4 减数
	 * @return 四个参数的差
	 */
	public static final double sub(double v1, double v2, double v3, double v4) {
		return add(v1, -v2, -v3, -v4, 0, 0, 4);
	}

	/**
	 * 功能说明：提供精确的减法运算。
	 * @param v1 被减数
	 * @param v2 减数
	 * @param v3 减数
	 * @param v4 减数
	 * @param v5 减数
	 * @return 五个参数的差
	 */
	public static final double sub(double v1, double v2, double v3, double v4, double v5) {
		return add(v1, -v2, -v3, -v4, -v5, 0, 5);
	}

	/**
	 * 功能说明：提供精确的减法运算。
	 * @param v1 被减数
	 * @param v2 减数
	 * @param v3 减数
	 * @param v4 减数
	 * @param v5 减数
	 * @param v6 减数
	 * @return 六个参数的差
	 */
	public static final double sub(double v1, double v2, double v3, double v4, double v5, double v6) {
		return add(v1, -v2, -v3, -v4, -v5, -v6, 6);
	}

	/**
	 * 功能说明：提供精确的乘法运算。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @return 两个参数的积
	 */
	public static final double mul(double v1, double v2) {
		return mul(v1, v2, 1, 1, 1, 1, 2);
	}

	/**
	 * 功能说明：提供精确的乘法运算。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @param v3 乘数
	 * @return 三个参数的积
	 */
	public static final double mul(double v1, double v2, double v3) {
		return mul(v1, v2, v3, 1, 1, 1, 3);
	}

	/**
	 * 功能说明：提供精确的乘法运算。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @param v3 乘数
	 * @param v4 乘数
	 * @return 四个参数的积
	 */
	public static final double mul(double v1, double v2, double v3, double v4) {
		return mul(v1, v2, v3, v4, 1, 1, 4);
	}

	/**
	 * 功能说明：提供精确的乘法运算(此方法为底层调用方法)。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @param v3 乘数
	 * @param v4 乘数
	 * @param v5 乘数
	 * @return 五个参数的积
	 */
	public static final double mul(double v1, double v2, double v3, double v4, double v5) {
		return mul(v1, v2, v3, v4, v5, 1, 5);
	}

	/**
	 * 功能说明：提供精确的乘法运算(此方法为底层调用方法)。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @param v3 乘数
	 * @param v4 乘数
	 * @param v5 乘数
	 * @param v6 乘数
	 * @return 六个参数的积
	 */
	public static final double mul(double v1, double v2, double v3, double v4, double v5, double v6) {
		return mul(v1, v2, v3, v4, v5, v6, 6);
	}

	/**
	 * 功能说明：提供精确的乘法运算(此方法为底层调用方法)。
	 * @param v1 被乘数
	 * @param v2 乘数
	 * @param v3 乘数
	 * @param v4 乘数
	 * @param v5 乘数
	 * @param v6 乘数
	 * @param n  参数个数
	 * @return n个参数的积
	 */
	private static final double mul(double v1, double v2, double v3, double v4, double v5, double v6, int n) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		BigDecimal b3 = null;
		BigDecimal b4 = null;
		BigDecimal b5 = null;
		BigDecimal b6 = null;
		double result = 0;

		switch (n) {
		case 2:
			result = b1.multiply(b2).doubleValue();
			break;
		case 6:
			b6 = new BigDecimal(Double.toString(v6));
		case 5:
			b5 = new BigDecimal(Double.toString(v5));
		case 4:
			b4 = new BigDecimal(Double.toString(v4));
		case 3:
			b3 = new BigDecimal(Double.toString(v3));
		}

		switch (n) {
		case 6:
			result = b1.multiply(b2).multiply(b3).multiply(b4).multiply(b5).multiply(b6).doubleValue();
			break;
		case 5:
			result = b1.multiply(b2).multiply(b3).multiply(b4).multiply(b5).doubleValue();
			break;
		case 4:
			result = b1.multiply(b2).multiply(b3).multiply(b4).doubleValue();
			break;
		case 3:
			result = b1.multiply(b2).multiply(b3).doubleValue();

		}
		return adjustDouble(result);
	}

	/**
	 * 功能说明：提供（相对）精确的除法运算，当发生除不尽的情况时，精确到15位有效数字
	 * @param v1 被除数
	 * @param v2  除数
	 * @return 两个参数的商
	 */
	public static final double div(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE);
	}

	/**
	 * 功能说明：提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。
	 * @param v1 被除数
	 * @param v2 除数
	 * @param scale 多少位有效数字。
	 * @return 两个参数的商
	 */
	public static final double div(double v1, double v2, int scale) {
		// 用BigDecimal的divide精度可以选择小数点后的位数，但实际有意义的确实有效位数
		// 这里获取除法结果的起始有效数字位置，结合scale有效位数，计算出需要的小数精度
		String strRes = null;
		int d;

		if (scale < 0){
			scale = DEF_DIV_SCALE;
		}

		strRes = String.valueOf(v1 / v2).toUpperCase(); // 在浮点精度范围内，v1/v2的有效数字起始位置是准确的，可以直接用来判断
		d = strRes.indexOf('.');
		if (strRes.length() - 1 < DEF_DIV_SCALE || d < 0)
			return v1 / v2;
		// 要保留的小数位数为 i + scale - 1
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 功能说明：Math.pow的结果最多可以有17位有效数字，本函数控制最多15位
	 * @param v1 double
	 * @param v2 double
	 * @return double
	 */
	public static final double pow(double v1, double v2) {
		return adjustDouble(Math.pow(v1, v2));
	}

	/**
	 * 功能说明：把java产生的double值转换成15位最多有效数字的值，多于小数四舍五入，整数不处理
	 * @param v1 double 要转换的参数值
	 * @return double
	 */
	public static final double adjustDouble(double v1) {

		String strRes = String.valueOf(v1).toUpperCase();
		int i, len = strRes.length();
		int e = strRes.lastIndexOf('E'); // 考虑科学记数法!
		int d = strRes.indexOf('.');

		if (len <= DEF_DIV_SCALE || d < 0)
			return v1;

		for (i = 0; i < len; i++)
			if (strRes.charAt(i) >= '1' && strRes.charAt(i) <= '9')
				break;
		i = len - (d > i ? i + 1 : i) - (e >= 0 ? len - e : 0);// 得出有效位数
		if (i > DEF_DIV_SCALE) {
			if (e < 0)
				v1 = round(v1, len - d - 1 - i + DEF_DIV_SCALE);
			else // 带科学记数法的处理: 先处理E前面的部分，再附加E部分，再转换成double
				v1 = round(v1, e - d - 1 - Integer.parseInt(strRes.substring(e + 1)) - (i - DEF_DIV_SCALE));
		}

		return v1;
	}

	/**
	 * 功能说明：比较两个double数值的大小
	 * @param v1 double类型参数1
	 * @param v2 double类型参数2
	 * @return 1,0或者-1，分别代表>,=和<
	 */
	public static final int compare(double v1, double v2) {
		return new BigDecimal(Double.toString(v1)).compareTo(new BigDecimal(Double.toString(v2)));
	}

	/**
	 * 功能说明：提供精确的小数位四舍五入处理。
	 * @param v  需要四舍五入的数字
	 * @param lDecs 小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static final double round(double v, int lDecs, boolean bTrunc) {
		if (lDecs < 0){
			return v;
		}
		BigDecimal b = new BigDecimal(Double.toString(v));
		BigDecimal one = new BigDecimal("1");
		return b.divide(one, lDecs, bTrunc ? BigDecimal.ROUND_DOWN : BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 功能说明：提供精确的小数位四舍五入处理。
	 * @param v 需要四舍五入的数字
	 * @param lDecs 小数点后保留几位
	 * @return double类型的四舍五入后的结果
	 */
	public static final double round(double v, int lDecs) {
		return round(v, lDecs, false);
	}
	
	/*
	 * public static void main(String arg[]){ double a = 2l; double b = 3;
	 * System.out.println(pow(a,b)); System.out.println(add(a,b));
	 * System.out.println(sub(a,b)); System.out.println(mul(a,b));
	 * System.out.println(round(div(a,b),2)); }
	 */

}
